/*
 * @Author: lll 1280388921@qq.com
 * @Date: 2025-04-10 21:54:06
 * @LastEditors: lll 1280388921@qq.com
 * @LastEditTime: 2025-04-13 20:03:32
 * @FilePath: /lesson27/2.pipe_use/ProcessPool.cc
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
#include "Task.hpp"
#include<string>
#include<cstring>
#include<vector>
#include<unistd.h>
#include<cstdlib>
#include<assert.h>
#include<iostream>
#include<ctime>
#include<sys/wait.h>
#include<sys/stat.h>

const int processnum=5;
std::vector<task_t> tasks;


//先描述
class Channel
{
public:
    Channel(int cmdfd,pid_t slaverid,const std::string &processname)
    :_cmdfd(cmdfd),_slaverid(slaverid),_processname(processname)
    {}
public:
    int _cmdfd;             //发送任务的文件描述符
    pid_t _slaverid;        //子进程的pid
    std::string _processname;    //子进程的名字 -- 方便我们打印日志
};

//再组织
void slaver()
{
    //read(0)
    //未来就可以直接在0里读管道的数据了
    while(1)
    {
        // std::cout<<"pid:"<<getpid()<<" ,read fd is:"<<rfd<<std::endl;
        //每个进程打出来的文件描述符都是3
        //正常的！
        //父进程的文件描述符是3/4
        //然后关闭读，就是关闭3
        //然后第二次父进程就是读3，写5
        //继续关闭读，关闭3
        //然后第二次父进程就是读3，写6
        //所以每次子进程读端拿到的都是3
        //……

        int cmdcode=0;
        int n=read(0,&cmdcode,sizeof(int));
        //如果父进程不给子进程发任务，子进程就会在read里阻塞等待！
        if(n==sizeof(cmdcode))
        {
            //执行cmdcode对应的任务列表
            // std::cout<<"slaver say : get a msg cmd: "<<getpid()<<" : cmdcode:"<<cmdcode<<std::endl;
            if(cmdcode>=0&&cmdcode<tasks.size())
            {
                tasks[cmdcode]();
            }
        }
        if(n==0)break;

        sleep(1);
    }
}

//输入型参数 const &
//输出型参数 *
//输入输出型参数 &
void InitProcessPool(std::vector<Channel> *channels)
{
    //version 2 不想出现一个管道有多个写端的情况 
    //（只想让一个管道只有一个写端一个读端）
    std::vector<int> oldfds;
    // 1.初始化
    for(int i=0;i<processnum;i++)
    {
        int pipefd[2];//临时空间
        int n=pipe(pipefd);
        assert(!n);
        (void)n;
        pid_t id=fork();
        if(id==0)//child
        {
            std::cout<<"child: "<<getpid()<<" close histroy fd: ";
            for(auto fd:oldfds)
            {
                std::cout<<fd<<" ";
                close(fd);
            }
            std::cout<<"\n";
            close(pipefd[1]);
            dup2(pipefd[0],0);
            //不要从标准输入里读，要从管道里读。
            slaver();
            std::cout<<"process : "<<getpid()<<" quit"<<std::endl;
            exit(0);
        }
        //father
        close(pipefd[0]);
        //添加channels字段
        std::string name="process-"+std::to_string(i);
        channels->push_back(Channel(pipefd[1],id,name));
        oldfds.push_back(pipefd[1]);
        
        sleep(1);
    }

}

void debug(const std::vector<Channel> &channels)
{
    //test
    for(const auto & c:channels)
    {
        std::cout<<"fd: "<<c._cmdfd<<" ,pid: "<<c._slaverid<<" ,name:"<<c._processname<<std::endl;
    }
}

void Menu()
{
    std::cout<<"-------------------------------------------------"<<std::endl;
    std::cout<<"----------------   1.刷新日志  ------------------"<<std::endl;
    std::cout<<"----------------   2.刷新野怪  ------------------"<<std::endl;
    std::cout<<"----------------   3.更新软件  ------------------"<<std::endl;
    std::cout<<"-------       4.刷新用户血量和蓝量  --------------"<<std::endl;
    std::cout<<"----------------     0.退出    ------------------"<<std::endl;
    std::cout<<"-------------------------------------------------"<<std::endl;
}

void CtrlSlaver(const std::vector<Channel> &channels)
{
    // int cnt=5;
    // 2.控制子进程
    int pos=0;
    while(1)
    {
        Menu();
        int select=0;
        std::cout<<"please enter:"<<std::endl;
        std::cin>>select;

        if(select<=0||select>=5)break;

        //1.选择任务
        int cmdcode=select-1;

        //2.选择进程
        //负载均衡 -- 让每个进程都可以较为平均分配到任务

        //1.随机数
        // int processpos=rand()%channels.size();

        //2.轮转
        int processpos=pos;

        std::cout<<"father say : cmdcode: "<<cmdcode<<"already send to: "<<channels[processpos]._slaverid<<" ,processname:"<<channels[processpos]._processname<<std::endl;

        //3.发送任务
        write(channels[processpos]._cmdfd,&cmdcode,sizeof(cmdcode));

        pos++;
        pos%=channels.size();

        // cnt--;
        sleep(1);
    }
}

void QuitProcess(const std::vector<Channel> &channels)
{
    for(auto c:channels)
    {
        close(c._cmdfd);
        waitpid(c._slaverid,nullptr,0);
    }
    // int last =channels.size()-1;
    // for(int i=last;i>=0;i--)
    // {
    //     close(channels[i]._cmdfd);
    //     waitpid(channels[i]._slaverid,nullptr,0);
    // }

    // for(auto c:channels)close(c._cmdfd);
    // sleep(5);
    // for(auto c:channels)waitpid(c._slaverid,nullptr,0);
    // sleep(5);
}


int main()
{
    LoadTask(&tasks);
    srand(time(nullptr)^getpid()^1023);//种下一个随机数的种子
    std::vector<Channel> channels;

    //1.初始化
    InitProcessPool(&channels);

    // debug(channels);

    // 2.控制子进程
    CtrlSlaver(channels);

    // 3.清理收尾
    QuitProcess(channels);

    // sleep(1000);
    return 0;
}